/*
 * @Author: zzd993
 * @Date: 2022-11-21 14:20:28
 * @LastEditors: zzd993
 * @LastEditTime: 2022-12-24 18:01:53
 * @FilePath: \vue设计与实现\practice.ts
 * Copyright (c) 2022 by ZZD, All Rights Reserved. 
 */
export {}

/**
 * 基础类型
 */

let isDone: boolean = false // 布尔
let num: number = 6 // 数字
let str: string = '6' // 字符串
let numArr: number[] = [1,3,4] // 数组
let strArr: Array<string> = ['1','6'] // 数组
let x: [string, number] = ['6',6] // 元组，当访问越界元素会使用联合类型代替
enum Color {
    Red, Green, Blue
} // 枚举
let notsure: any = 5  // any
function warnUser(): void {}   // void
let unuseable: void = undefined // 声明void变量没啥用，只能赋值为null、undefined
/**
 * undefined null 
 * 如果指定了--strictNullChecks标记，null和undefined只能赋值给void和它们各自
 */
let u: undefined = undefined
let n: null = null
let strOrUndefined: string | undefined = '6'
/**
 * never
 * 表示的是那些永不存在的值的类型。
 */
// 返回never的函数必须存在无法达到的终点
function error(msg: string): never {
    throw new Error(msg)
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}
declare function create(o: object | null): void  // object
// create({})
// create(3)
/**
 * 类型断言
 */
let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length



/**
 * 接口
 */

/**
 * 类型检查器会查看printLabel的调用。 printLabel有一个参数，
 * 并要求这个对象参数有一个名为label类型为string的属性。 
 * 需要注意的是，我们传入的对象参数实际上会包含很多属性，
 * 但是编译器只会检查那些必需的属性是否存在，并且其类型是否匹配。 
 * 然而，有些时候TypeScript却并不会这么宽松，我们下面会稍做讲解。
 */
function printLabel(labelledObj: { label: string }): void {
    console.log(labelledObj.label)
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj)

/**
 * 下面我们重写上面的例子，这次使用接口来描述：必须包含一个label属性且类型为string：
 * LabelledValue接口就好比一个名字，用来描述上面例子里的要求。 
 * 它代表了有一个 label属性且类型为string的对象。 
 * 需要注意的是，我们在这里并不能像在其它语言里一样，说传给 printLabel的对象实现了这个接口。
 * 我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件，那么它就是被允许的。
 */
interface LabelledValue {
    label: string;
}

function printLabel2(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

let myObj2 = {size: 10, label: "Size 10 Object"};
printLabel2(myObj2);

// 可选属性
interface SquareConfig {
    color?: string
    width?: number
}
// 只读属性
interface Point {
    readonly x: number
    readonly y: number
}
let p1: Point = { x: 1, y: 2 }
// p1.x = 3 报错

/**
 * 接口除了能描述普通对象，也能描述函数类型
 */
interface SearchFunc {
    (source: string, subString: string): boolean
}
// 注意：定义的形参名不需要与接口的名字相同
let mySearch: SearchFunc = (source: string, subString: string) => {
    let res = source.search(subString)
    return res > -1
}

/**
 * 类类型
 * 类用implements实现接口
 * 类有静态类型和实例类型
 */
interface ClockInterface {
    currentTime: Date
    setTime(d: Date): void
}
class Clock implements ClockInterface {
    currentTime: Date
    setTime(d: Date) {
        this.currentTime = d
    }
    constructor(h: number, m: number) {}
}

interface ClockConstructor2 {
    new (hour: number, minute: number): any;
}
/**
 * 这里会报错。因为当一个类实现了一个接口时，只对其实例部分进行类型检查。 
 * constructor存在于类的静态部分，所以不在检查的范围内
 */
// class Clock2 implements ClockConstructor2 {
//     currentTime: Date;
//     constructor(h: number, m: number) { }
// }

/**
 * 接口继承
 */
interface shape {
    color: string
}
interface Square extends shape {
    sideLength: number
}
let square = <Square>{}
square.color = 'red'
square.sideLength = 3

/**
 * 混合类型
 */
interface Counter {
    (start: number): string
    interval: number
    reset(): void
}
function getCounter(): Counter {
    let counter = <Counter>function(start: number) {}
    counter.interval = 123
    counter.reset = function() {}
    return counter
}
let c = getCounter()
c(10)
c.reset()
c.interval = 5

/**
 * 接口继承类
 * 当接口继承了一个类类型时，它会继承类的成员但不包括其实现。
 * 接口同样会继承到类的private和protected成员。 
 * 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时，
 * 这个接口类型只能被这个类或其子类所实现（implement）
 */
class Control {
    private state: any
}
interface SelectableControl extends Control {
    select(): void
}
class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// 错误：“Image”类型缺少“state”属性。
// class Image implements SelectableControl {
//     select() { }
// }

class Location {}



/**
 * 类
 */
class Greeter {
    greeting: string
    constructor(msg: string) {
        this.greeting = msg
    }
    greet() {
        return 'hello, ' + this.greeting
    }
}

let greeter = new Greeter('world')

// 继承
class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

// public 公开的
class Animal2 {
    public name: string
    public constructor(name: string) {
        this.name = name
    }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`)
    }
}
// private 不能在声明它的类的外部访问
class Animal3 {
    private name: string
    constructor(name: string) {
        this.name = name
    }
}
// new Animal3('cat').name 错误，name是私有的
class R extends Animal3 {
    constructor() {
        super('R')
    }
}
// new R().name 错误
// protected 与private类似，但在派生类中仍可访问
class Person {
    protected name: string
    constructor(name: string) {
        this.name = name
    }
}
class Employee extends Person {
    private department: string
    constructor(name: string, department: string) {
        super(name)
        this.department = department
    }
    public getElevatorPitch() {
        return '6'
    }
}
// console.log(Employee.name);
let zzd = new Employee('zzd', 'sales')
console.log(zzd.getElevatorPitch());
// console.log(zzd.name);  属性“name”受保护，只能在类“Person”及其子类中访问。

// readonly 只读属性，必须在声明时或构造函数里被初始化
class Octopus {
    readonly name: string
    readonly numberOfLegs: number = 8
    constructor(name: string) {
        this.name = name
    }
    // constructor(readonly name: string) { ... } 可以这样写
}
let dad = new Octopus('3')
// dad.name = '2'  错误，是只读的

// 存取器
let passcode = '6'
class Employee2 {
    private _fullName: string
    get fullName(): string {
        return this._fullName
    }
    set fullName(newName: string) {
        if(passcode && passcode == '6') {
            this._fullName = newName
        } else {
            console.log(666);
        }
    }
}
let employee2 = new Employee2()
employee2.fullName = '6'
// 以上讨论了类的实例属性，仅当类被实例化的时候才会被初始化的属性。
// 接下来看类的静态属性，这些属性存在于类本身而不存在于实例上
class Grid {
    static o: number = 3
}
// console.log(Grid.o);

// 抽象类
// 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 
// 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。
abstract class Animal4 {
    abstract makeSound(): void
    static move(): void {
        console.log(6)
    }
}
class Pig extends Animal4 {
    makeSound(): void {
        console.log('lululu')
    }
}
console.log(Pig.move());



/**
 * 函数
 */

// 完整定义
let myAdd: (baseValue: number, increment: number) => number = 
    function(x: number, y: number): number { return x+y }

// 可选参数：? ， 默认参数=， 默认参数与前面的必须参数或可选参数共享参数类型
// 剩余参数
function buildName(first: string, ...rest: string[]) {}

// this和箭头函数
let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

// alert("card: " + pickedCard.card + " of " + pickedCard.suit)
// 重载 略



/**
 * 泛型
 */

function identify<T>(arg: T): T { return arg }
let output = identify<string>('zzd')
let output2 = identify('zzd2') // 会自动推断，更普遍使用

function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}
// 泛型类
class GenericNumber<T> {
    zeroValue: T
    add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.add = function(x, y) { return x + y; }
myGenericNumber.zeroValue = 0
console.log(myGenericNumber);

// 泛型约束
interface Lengthwise {
    length: number
}
// 创建一个包含 .length属性的接口，使用这个接口和extends关键字来实现约束
function loggingIdentity2<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}


/**
 * 枚举
 */

// 数字枚举
enum Direction {
    Up = 1,    //默认从0开始
    Down,
    Left,
    Right
}
// 字符串枚举
enum Direction2 {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
// const枚举
const enum Enum {
    A = 1,
    B = A * 2
}


/**
 * 类型兼容性
 */

// 基本规则，如果x要兼容y，那么y至少具有与x相同的属性。
interface Named {
    name: string
}
let x2: Named
let y = { name: '6', location: '6' }
x2 = y



/**
 * 高级类型
 */
// 1. 交叉类型
function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{}
    for(let id in first) {
        (<any>result)[id] = (<any>first)[id]
    }
    for(let id in second) {
        if(!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id]
        }
    }
    return result
}
class Person2 {
    constructor(public name: string) {}
}
interface Loggable {
    log(): void
}
class ConsoleLogger implements Loggable {
    log(): void {
        //...
    }
}
let jim = extend(new Person2('jim'), new ConsoleLogger())
let n2 = jim.name
jim.log()
// 2. 联合类型，只能访问联合类型的所有类型里共有的成员。
function padLeft(value: string, padding: string | number) {}
// 3. typeof
function padLeft2(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}
// 4. instanceof
interface Padder {
    getPaddingString(): string
}

class SpaceRepeatingPadder implements Padder {
    constructor(private numSpaces: number) { }
    getPaddingString() {
        return Array(this.numSpaces + 1).join(" ");
    }
}

class StringPadder implements Padder {
    constructor(private value: string) { }
    getPaddingString() {
        return this.value;
    }
}

function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
}

// 类型为SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
    padder; // 类型细化为'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // 类型细化为'StringPadder'
}

// 类型别名
// 类型别名有时和接口很像，但是可以作用于原始值，联合类型，元组以及其它任何你需要手写的类型。
type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver
type Tree<T> = {
    value: T
    left: Tree<T>
    right: Tree<T>
}
// 与接口的区别
// 一，接口创建了一个新的名字，可以在其它任何地方使用。 
// 类型别名并不创建新名字—比如，错误信息就不会使用别名。 
type Alias = { num: number }
interface Interface {
    num: number
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
// 另一个重要区别是类型别名不能被 extends和 implements
//（自己也不能 extends和 implements其它类型）。 

// 字符串字面量类型
type Easing = "ease-in" | "ease-out" | "ease-in-out"

// 多态的this类型
// 表示的是某个包含类或接口的子类型。它能很容易连贯接口间的继承。
class BasicCalulator {
    constructor(protected value: number = 0) {}
    currentValue(): number {
        return this.value
    }
    add(operand: number): this {
        this.value += operand
        return this
    }
}
let v = new BasicCalulator(2).add(1).add(2)
console.log(v)

// 索引类型
// keyof T， 索引类型查询操作符。 
// 对于任何类型 T， keyof T的结果为 T上已知的公共属性名的联合
interface Person3 {
    name: string;
    age: number;
}
let personProps: keyof Person3; // 'name' | 'age'
// T[K]， 索引访问操作符


// 映射类型
interface PersonPartial {
    name?: string;
    age?: number;
}
interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}
// type Readonly<T> = {
//     readonly [P in keyof T]: T[P];
// }
// type Partial<T> = {
//     [P in keyof T]?: T[P];
// }
type PersonPartial2 = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
/*
Exclude<T, U> -- 从T中剔除可以赋值给U的类型。
Extract<T, U> -- 提取T中可以赋值给U的类型。
NonNullable<T> -- 从T中剔除null和undefined。
ReturnType<T> -- 获取函数返回值类型。
InstanceType<T> -- 获取构造函数类型的实例类型。
*/
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"

type T02 = Exclude<string | number | (() => void), Function>;  // string | number
type T03 = Extract<string | number | (() => void), Function>;  // () => void

type T04 = NonNullable<string | number | undefined>;  // string | number
type T05 = NonNullable<(() => string) | string[] | null | undefined>;  // (() => string) | string[]

function f1(s: string) {
    return { a: 1, b: s };
}

class C {
    x = 0;
    y = 0;
}

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
type T15 = ReturnType<any>;  // any
type T16 = ReturnType<never>;  // any
// type T17 = ReturnType<string>;  // Error
// type T18 = ReturnType<Function>;  // Error

type T20 = InstanceType<typeof C>;  // C
type T21 = InstanceType<any>;  // any
type T22 = InstanceType<never>;  // any
// type T23 = InstanceType<string>;  // Error
// type T24 = InstanceType<Function>;  // Error



/**
 * 模块
 */
export interface StringValidator {
    isAcceptable(s: string): boolean
}
const numberRegexp = /^[0-9]+$/;
class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator as mainValidator }

// 准备面试

let foo: unknown = 123

// 赶紧给个offer吧，太心累了
// 这周给offer，回家过好年好吧
// 羊了，贼难受啊